using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using AutoMapper;
using microsoft_TODO.DTOs;
using microsoft_TODO.ServiceCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using server.Helpers;

namespace microsoft_TODO.Controllers {

    [Route ("api/[controller]")]
    public class UserController : Controller {

        ILogger<UserController> _logger;
        IUserService _userService;
        IMapper _mapper;
        IConfiguration _configuration;

        public UserController (ILogger<UserController> logger,
            IUserService userService,
            IMapper mapper,
            IConfiguration configuration) {
            _logger = logger;
            _userService = userService;
            _mapper = mapper;
            _configuration = configuration;
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="dto"></param>
        /// <remarks>
        /// codes:
        ///
        ///     4003: "用户不存在或密码错误"
        ///
        /// </remarks>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost ("auth")]
        public IActionResult Login ([FromBody] LoginDto dto) {
            try {
                var entity = _userService.Authenticate (dto);
                if (entity == null) {
                    _logger.LogWarning(LoggingEvents.GetItemNotFound, "Login({name}) Failed", dto.Username);
                    return NotFound(new { code = LoggingEvents.GetItemNotFound });
                }
                var token = GenerateToken (entity.Id);
                var result = _mapper.Map<LoginResultDto> (entity);
                result.token = token;
                return Ok (result);
            } catch (Exception) {
                _logger.LogError ("Failed to execute GET");
                return BadRequest ();
            }
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="dto"></param>
        /// <remarks>
        /// codes:
        ///
        ///     2001: "用户名已被注册"
        ///
        /// </remarks>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost ("register")]
        public IActionResult Register ([FromBody] RegisterDto dto) {
            try {
                if (_userService.IsUsernameExistent (dto.Username)) {
                    _logger.LogWarning(LoggingEvents.ItemExistence, "Username({name}) Is Existent", dto.Username);
                    return new ObjectResult (new { succ = false, code = LoggingEvents.ItemExistence });
                }
                if (_userService.CreateUser (dto)) {
                    _logger.LogInformation(LoggingEvents.InsertItem, "User({name}) Inserted", dto.Username);
                    return Created ("", new { succ = true });
                }
                return BadRequest();
            } catch (Exception ex) {
                _logger.LogError (ex.Message);
                return BadRequest ();
            }
        }

        [HttpPut]
        public IActionResult Put ([FromBody] Models.User model) {
            try {
                return Ok ();
            } catch (Exception) {
                _logger.LogError ("Failed to execute PUT");
                return BadRequest ();
            }
        }

        private TokenResult GenerateToken (string id) {
            var claims = new Claim[] {
                new Claim (ClaimTypes.NameIdentifier, id),
            };

            var now = DateTime.UtcNow;

            SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey (Encoding.UTF8.GetBytes (_configuration.GetSection ("Jwt") ["Secret"])); // 生成密钥
            SigningCredentials signingCredential = new SigningCredentials (symmetricSecurityKey, SecurityAlgorithms.HmacSha256); // 生成登录证书

            var jwtToken = new JwtSecurityToken (
                claims : claims,
                notBefore : now,
                expires : now.Add (TimeSpan.FromHours (1)),
                signingCredentials : signingCredential
            ); // 生成token

            var access_token = new JwtSecurityTokenHandler ().WriteToken (jwtToken);
            var response = new TokenResult {
                access_token = access_token,
                expires_in = TimeSpan.FromHours (1).TotalDays
            };

            return response;
        }
    }
}